package org.gjy.m8.io;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

/**
 * @author gjy
 * @version 1.0
 * @since 2025-11-10 15:52:48
 */
public class ZipUtil {

    private static final int BUFFER_SIZE = 8192;

    public static void compress(List<File> source, File zipFile) {
        byte[] buffer = new byte[BUFFER_SIZE];
        try {
            ZipOutputStream zos = new ZipOutputStream(Files.newOutputStream(zipFile.toPath()));
            for (File file : source) {
                FileInputStream fis = new FileInputStream(file);
                zos.putNextEntry(new ZipEntry(file.getName()));
                int len;
                while ((len = fis.read(buffer)) != -1) {
                    zos.write(buffer, 0, len);
                }
                zos.closeEntry();
                fis.close();
            }
            zos.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public static void decompress(File zipFile, String target) {
        if (!Files.exists(zipFile.toPath())) {
            throw new IllegalArgumentException("压缩文件不存在");
        }

        try (ZipFile zip = new ZipFile(zipFile, StandardCharsets.UTF_8)) {
            Path path = Paths.get(target);
            if (!Files.exists(path)) {
                Files.createDirectories(path);
            }

            Enumeration<? extends ZipEntry> entries = zip.entries();
            while (entries.hasMoreElements()) {
                ZipEntry entry = entries.nextElement();
                Path targetFile = path.resolve(entry.getName());
                if (entry.isDirectory()) {
                    if (!Files.exists(targetFile)) {
                        Files.createDirectories(targetFile);
                    }
                } else {
                    Path parentDir = targetFile.getParent();
                    if (parentDir != null && !Files.exists(parentDir)) {
                        Files.createDirectories(parentDir);
                    }

                    try (InputStream is = zip.getInputStream(entry);
                         OutputStream os = Files.newOutputStream(targetFile)) {
                        byte[] buffer = new byte[BUFFER_SIZE];
                        int bytesRead;

                        while ((bytesRead = is.read(buffer)) != -1) {
                            os.write(buffer, 0, bytesRead);
                        }
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 压缩单个文件为 ZIP
     *
     * @param sourceFilePath 源文件路径
     * @param zipFilePath    目标 ZIP 文件路径
     * @throws IOException 文件操作异常
     */
    public static void compress(String sourceFilePath, String zipFilePath) throws IOException {
        Path sourceFile = Paths.get(sourceFilePath);
        Path zipFile = Paths.get(zipFilePath);

        // 验证源文件
        if (!Files.exists(sourceFile)) {
            throw new FileNotFoundException("源文件不存在: " + sourceFilePath);
        }

        if (Files.isDirectory(sourceFile)) {
            throw new IllegalArgumentException("源路径是目录，不支持压缩: " + sourceFilePath);
        }

        // 创建父目录
        Path parentDir = zipFile.getParent();
        if (parentDir != null && !Files.exists(parentDir)) {
            Files.createDirectories(parentDir);
        }

        // 执行压缩
        try (FileOutputStream fos = new FileOutputStream(zipFile.toFile());
             ZipOutputStream zos = new ZipOutputStream(fos, StandardCharsets.UTF_8)) {

            String fileName = sourceFile.getFileName().toString();
            ZipEntry zipEntry = new ZipEntry(fileName);
            zos.putNextEntry(zipEntry);

            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead;

            try (FileInputStream fis = new FileInputStream(sourceFile.toFile())) {
                while ((bytesRead = fis.read(buffer)) != -1) {
                    zos.write(buffer, 0, bytesRead);
                }
            }

            zos.closeEntry();
        }
    }

    /**
     * 解压 ZIP 文件到目标目录（仅解压 ZIP 中的第一个文件）
     *
     * @param zipFilePath   ZIP 文件路径
     * @param targetDirPath 目标目录路径
     * @return 解压后的文件路径
     * @throws IOException 文件操作异常
     */
    public static String decompress(String zipFilePath, String targetDirPath) throws IOException {
        Path zipFile = Paths.get(zipFilePath);
        Path targetDir = Paths.get(targetDirPath);

        // 验证 ZIP 文件
        if (!Files.exists(zipFile)) {
            throw new FileNotFoundException("ZIP 文件不存在: " + zipFilePath);
        }

        // 创建目标目录
        if (!Files.exists(targetDir)) {
            Files.createDirectories(targetDir);
        }

        String extractedFilePath = null;

        try (ZipFile zip = new ZipFile(zipFile.toFile(), StandardCharsets.UTF_8)) {
            java.util.Enumeration<? extends ZipEntry> entries = zip.entries();

            if (!entries.hasMoreElements()) {
                throw new IOException("ZIP 文件为空: " + zipFilePath);
            }

            // 只解压第一个文件
            ZipEntry entry = entries.nextElement();

            // 跳过目录项
            if (entry.isDirectory()) {
                if (entries.hasMoreElements()) {
                    entry = entries.nextElement();
                } else {
                    throw new IOException("ZIP 文件中只有目录，没有文件");
                }
            }

            Path targetFile = targetDir.resolve(entry.getName());

            // 确保父目录存在
            Path parentDir = targetFile.getParent();
            if (parentDir != null && !Files.exists(parentDir)) {
                Files.createDirectories(parentDir);
            }

            // 解压文件
            try (InputStream is = zip.getInputStream(entry);
                 OutputStream os = Files.newOutputStream(targetFile.toFile().toPath())) {

                byte[] buffer = new byte[BUFFER_SIZE];
                int bytesRead;

                while ((bytesRead = is.read(buffer)) != -1) {
                    os.write(buffer, 0, bytesRead);
                }
            }

            extractedFilePath = targetFile.toString();
        }

        return extractedFilePath;
    }

}
